/* 
 * Copyright 2009 Marc Englund <marc.englundATitmill.com>
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

package org.vaadin.simplegesture;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import org.vaadin.simplegesture.client.ui.Common;

import com.vaadin.terminal.PaintException;
import com.vaadin.terminal.PaintTarget;
import com.vaadin.ui.AbstractComponent;
import com.vaadin.ui.Component;

/**
 * Server side component for the VSimpleGesture widget.
 */
@com.vaadin.ui.ClientWidget(org.vaadin.simplegesture.client.ui.VSimpleGesture.class)
public class SimpleGesture extends AbstractComponent {

    private static final Method GESTURE_METHOD;
    static {
        try {
            GESTURE_METHOD = GestureListener.class.getDeclaredMethod("gesture",
                    new Class[] { GestureEvent.class });
        } catch (final java.lang.NoSuchMethodException e) {
            // This should never happen
            throw new java.lang.RuntimeException(
                    "Internal error finding methods in SimpleGesture");
        }
    }

    private Component target = null;
    private int maxDistance = Common.MAXDISTANCE;
    private boolean recordMode = false;
    private boolean normalizing = false;
    private HashMap<String, Object> gestures = new HashMap<String, Object>();

    public SimpleGesture() {
        this(null);
    }

    public SimpleGesture(Component target) {
        this.target = target;
    }

    public boolean isRecordMode() {
        return recordMode;
    }

    public void setRecordMode(boolean recordMode) {
        this.recordMode = recordMode;
        requestRepaint();
    }

    public boolean isNormalizing() {
        return normalizing;
    }

    public void setNormalizing(boolean normalizing) {
        this.normalizing = normalizing;
    }

    public void addGesture(String gesture) {

    }

    public void addGesture(String gesture, Object data) {
        addGesture(gesture, data, maxDistance);
    }

    public void addGesture(String gesture, Object data, int maxDistance) {
        if (gesture == null) {
            return;
        }
        gestures.put(gesture, data);
        requestRepaint();
    }

    public void removeGesture(String gesture) {
        gestures.remove(gesture);
        requestRepaint();
    }

    public Set getAllGestures() {
        return this.gestures.keySet();
    }

    public void clear() {
        this.gestures.clear();
        requestRepaint();
    }

    public void setDefaultMaxDistance(int maxDistance) {
        this.maxDistance = maxDistance;
        requestRepaint();
    }

    public int getDefaultMaxDistance() {
        return this.maxDistance;
    }

    @Override
    public void paintContent(PaintTarget target) throws PaintException {
        super.paintContent(target);
        target.addAttribute(Common.ATTR_MAXDISTANCE, maxDistance);
        target.addAttribute(Common.ATTR_RECORD, recordMode);
        target.addAttribute(Common.ATTR_NORMALIZE, normalizing);
        if (this.target != null) {
            target.paintReference(this.target, Common.TARGET_REF);
        }
        target.addVariable(this, Common.VAR_MATCHED, "");
        target.addVariable(this, Common.VAR_MOVED, "");
        target.addVariable(this, Common.VAR_DISTANCE, "");
        target.startTag(Common.GESTURES_TAG);
        for (String g : gestures.keySet()) {
            target.startTag(Common.GESTURE_TAG);
            target.addAttribute(Common.GESTURE_ATTR_MOVES, g);
            target.endTag(Common.GESTURE_TAG);
        }
        target.endTag(Common.GESTURES_TAG);
    }

    @Override
    public void changeVariables(Object source, Map variables) {
        super.changeVariables(source, variables);
        if (variables.containsKey(Common.VAR_MOVED)) {
            String moved = (String) variables.get(Common.VAR_MOVED);
            String matched = (String) variables.get(Common.VAR_MATCHED);
            int distance = (Integer) variables.get(Common.VAR_DISTANCE);
            if (recordMode || gestures.containsKey(matched)) {
                Object data = gestures.get(matched);
                GestureEvent evt = new GestureEvent(this, matched, moved,
                        distance, data);
                fireEvent(evt);
            }
        }
    }

    public void addListener(GestureListener listener) {
        super.addListener(GestureEvent.class, listener, GESTURE_METHOD);
    }

    public void removeListener(GestureListener listener) {
        super.removeListener(GestureEvent.class, listener, GESTURE_METHOD);
    }

    public class GestureEvent extends Event {
        private static final long serialVersionUID = -4766249038615119101L;
        private String matched;
        private String moved;
        private int distance;
        private Object data;

        public GestureEvent(SimpleGesture source, String matched, String moved,
                int distance, Object data) {
            super(source);
            this.data = data;
            this.matched = matched;
            this.moved = moved;
            this.distance = distance;
        }

        public String getMatched() {
            return matched;
        }

        public String getMoved() {
            return moved;
        }

        public int getDistance() {
            return distance;
        }

        public Object getData() {
            return data;
        }

    }

    public interface GestureListener {
        public void gesture(GestureEvent event);
    }

}
